<?php declare(strict_types=1);
/**
 * @author      xianganyall <xianganyall@gmail.com>
 * @copyright   2023-2025 owner
 **/

namespace Srv\Libs\Frame;

use Srv\Libs\Common\CommValid;
use Srv\Libs\ConfModel\ProjectConf;
use Srv\Libs\ConfModel\WebSocketConf;
use Swoole\Constant;
use Swoole\Coroutine;
use Swoole\Server\Task;
use Swoole\Websocket\Server;
use Swoole\WebSocket\Frame;
use Swoole\Http\Request;
use Swoole\Http\Response;

final class WebSocketService
{
    private static ?WebSocketService $WebSocketService          = null;                 // WebSocketService
    private static string $processNamePrefix                    = 'Srv-';               // 进程前缀
    private static string $processNameMaster                    = 'Master';             // 主进程
    private static string $processNameWorker                    = 'Worker';             // 工作进程
    private static string $processNameTask                      = 'Task';               // 任务进程
    private static array $serverRunConf                         = [];                   // 服务运行配置
    private const SET_OPTION_HOOK_FLAGS                         = 0; // SWOOLE_HOOK_ALL|SWOOLE_HOOK_CURL; // 开启协程模式包括[CURL]

    /**
     * @param WebSocketConf $WebSocketConf
     * @param ProjectConf $ProjectConf
     * @return bool
     * 启动服务
     */
    public static function init(WebSocketConf $WebSocketConf, ProjectConf $ProjectConf):bool
    {
        $__class__                      = self::class;
        self::$WebSocketService         = new $__class__();
        $listenServer                   = $WebSocketConf->getHost();
        $listenPort                     = $WebSocketConf->getListen();
        $serverConf                     = [
            'worker_num'                    => $WebSocketConf->getWorkerNum(),              // 工作进程数
            'task_worker_num'               => $WebSocketConf->getTaskWorkerNum(),          // Task进程数
            'task_enable_coroutine'         => true,                                        // Task启用协程
            'task_use_object'               => true,                                        // Task启用对象风格
            'enable_coroutine'              => true,                                        // 启用协程
            'daemonize'                     => $WebSocketConf->isDaemonIze(),               // 是否守护模式
            'heartbeat_idle_time'           => $WebSocketConf->getHeartbeatIdleTime(),      // 空闲关闭时间[秒]
            'heartbeat_check_interval'      => $WebSocketConf->getHeartbeatCheckInterval(), // 间隔遍历时间[秒]
            'open_eof_check'                => $WebSocketConf->isOpenEofCheck(),            // EOF检测
            'open_length_check'             => $WebSocketConf->isOpenLengthCheck(),         // 包检查
            'package_max_length'            => $WebSocketConf->getPackageMaxLength(),       // 包最大长度[默认:2M]
            'buffer_output_size'            => $WebSocketConf->getBufferOutputSize(),       // End或Write最大长度[默认:2M]
            'package_length_offset'         => $WebSocketConf->getPackageLengthOffset(),    // 包头偏移数
            'package_length_type'           => $WebSocketConf->getPackageLengthType(),      // 格式类型[N=4]
            'package_body_offset'           => $WebSocketConf->getPackageBodyOffset(),      // 包内容偏移数
            'dispatch_mode'                 => $WebSocketConf->getDispatchMode(),           // 固定模式[1-轮循,2-固定,3-抢占,4-IP分配,5-UID分配,7-stream]
            'max_request'                   => $WebSocketConf->getMaxRequest(),             // 最大任务数
            'reload_async'                  => true,                                        // 异步重启开关
            'tcp_fastopen'                  => true,                                        // 开启TCP快速握手特性
            'max_wait_time'                 => $WebSocketConf->getMaxWaitTime(),            // 最大等待重启时长[秒]

            'http_parse_cookie'             => true,                                        // Cookie解析[默认:true]
            'http_parse_post'               => true,                                        // Post解析[默认:true]
            'http_parse_files'              => true,                                        // Files上传解析[默认:true]
            'http_compression'              => true,                                        // 压缩内容[算法优先级: br > gzip > deflate][默认:true]
            'http_compression_level'        => 1,                                           // 压缩等级[1-9][越大压缩出的内容越小][默认:1]
            'http_gzip_level'               => 1,                                           // 压缩等级[1-9][越大压缩出的内容越小][默认:1]
            'compression_min_length'        => 128,                                         // 超过该值才开启压缩[字节][默认:20]

            'upload_max_filesize'           => $WebSocketConf->getUploadMaxSize(),          // 上传文件的最大大小[字节]
            'upload_tmp_dir'                => rtrim($WebSocketConf->getUploadTmpPath(), '/'),  // 上传文件的临时目录[目录最大长度不得超过220字节]
            'document_root'                 => $WebSocketConf->getDocRootPath(),            // 文档根目录
            'http_autoindex'                => $WebSocketConf->isAutoIndex(),               // 启用索引文件列表
            'enable_static_handler'         => $WebSocketConf->isDocStatic(),               // 启用静态资源访问
            'static_handler_locations'      => $WebSocketConf->getDocStaticPaths(),         // 静态处理的目录
            'http_index_files'              => $WebSocketConf->getIndexFiles(),             // 被索引的文件列表
        ];
        // 启动参数优先
        $cliArgvList                    = Conf::getCliArgvList();
        if(count($cliArgvList) > 0){
            if(isset($cliArgvList['daemonize'])) $serverConf['daemonize'] = boolval($cliArgvList['daemonize']);
            if(isset($cliArgvList['worker_num'])) $serverConf['worker_num'] = intval($cliArgvList['worker_num']);
            if(isset($cliArgvList['task_worker_num'])) $serverConf['task_worker_num'] = intval($cliArgvList['task_worker_num']);
            if(isset($cliArgvList['max_request'])) $serverConf['max_request'] = intval($cliArgvList['max_request']);
            if(isset($cliArgvList['max_wait_time'])) $serverConf['max_wait_time'] = intval($cliArgvList['max_wait_time']);
        }
        // 进程前缀
        if(strlen($WebSocketConf->getProcessPrefix()) > 0) self::$processNamePrefix = $WebSocketConf->getProcessPrefix();
        // 队列长度
        if($WebSocketConf->getBacklog() > 0) $serverConf['backlog'] = $WebSocketConf->getBacklog();
        // 进程用户名
        if(strlen($WebSocketConf->getUser()) > 0) $serverConf['user'] = $WebSocketConf->getUser();
        // 进程用户组
        if(strlen($WebSocketConf->getGroup()) > 0) $serverConf['group'] = $WebSocketConf->getGroup();
        // 系统根目录
        if(strlen($WebSocketConf->getChRootPath()) > 0){
            $serverConf['chroot']           = $WebSocketConf->getChRootPath();
            $chrootLength                   = strlen($serverConf['chroot']);
            if($serverConf['chroot'] === substr($serverConf['upload_tmp_dir'], 0, $chrootLength)) $serverConf['upload_tmp_dir'] = substr($serverConf['upload_tmp_dir'], $chrootLength - 1);
        }
        // PID文件地址
        if(strlen($WebSocketConf->getPidFile()) > 0) $serverConf['pid_file'] = $WebSocketConf->getPidFile();
        // 日志文件地址
        if(strlen($WebSocketConf->getLogFile()) > 0) $serverConf['log_file'] = $WebSocketConf->getLogFile();
        // 日志等级[0-6]
        if(!$ProjectConf->isRelease()) $serverConf['log_level'] = 0;
        if($WebSocketConf->getLogLevel() >= 0) $serverConf['log_level'] = max(min($WebSocketConf->getLogLevel(), 6), 0);
        // 设置WebSocket子协议[Sec-WebSocket-Protocol]
        if(strlen($WebSocketConf->getSubProtocol()) > 0) $serverConf['websocket_subprotocol'] = $WebSocketConf->getSubProtocol();
        // 开启协程模式包括[CURL]
        if(self::SET_OPTION_HOOK_FLAGS > 0) Coroutine::set([Constant::OPTION_HOOK_FLAGS => self::SET_OPTION_HOOK_FLAGS]);
        // create server
        if(CommValid::isIp($listenServer)){         // tcp-ipv4
            $WebSocketServer                    = new Server($listenServer, $listenPort, SWOOLE_PROCESS, SWOOLE_TCP);
        }else if(CommValid::isIpv6($listenServer)){ // tcp-ipv6
            $WebSocketServer                    = new Server($listenServer, $listenPort, SWOOLE_PROCESS, SWOOLE_TCP6);
        }else{                                      // unix-sock
            $WebSocketServer                    = new Server($listenServer, 0, SWOOLE_PROCESS, SWOOLE_SOCK_UNIX_STREAM);
        }
        self::$serverRunConf                    = $serverConf;
        $WebSocketServer->set($serverConf);
        $WebSocketServer->on('Start',           [&self::$WebSocketService, 'start']);           // 启动服务
        $WebSocketServer->on('WorkerStart',     [&self::$WebSocketService, 'workerStart']);     // 启动工作进程
        $WebSocketServer->on('WorkerExit',      [&self::$WebSocketService, 'workerExit']);      // 停止工作进程
        $WebSocketServer->on('Request',         [&self::$WebSocketService, 'request']);         // 网络HTTP请求
        $WebSocketServer->on('Open',            [&self::$WebSocketService, 'open']);            // 新链接
        $WebSocketServer->on('Message',         [&self::$WebSocketService, 'message']);         // 新消息
        $WebSocketServer->on('Close',           [&self::$WebSocketService, 'close']);           // 关闭链接
        $WebSocketServer->on('Task',            [&self::$WebSocketService, 'taskStart']);       // 任务进程开始
        $WebSocketServer->on('Finish',          [&self::$WebSocketService, 'taskFinish']);      // 任务执行完成
        $WebSocketServer->on('BeforeReload',    [&self::$WebSocketService, 'reloadBefore']);    // 重载前回调
        $WebSocketServer->on('AfterReload',     [&self::$WebSocketService, 'reloadAfter']);     // 重载后回调
        ResponseMain::serviceInit($WebSocketServer);
        set_time_limit(0);
        $WebSocketServer->start();
        return true;
    }

    /**
     * @param Server $WebSocketServer
     * @return void
     * 回调服务启动
     */
    public function start(Server $WebSocketServer)
    {
        swoole_set_process_name(self::$processNamePrefix.self::$processNameMaster);
        ResponseMain::serviceStart($WebSocketServer);
    }

    /**
     * @param Server $WebSocketServer
     * @param int $workId
     * @return void
     * 指定进程处理逻辑
     */
    public function workerStart(Server $WebSocketServer, int $workId)
    {
        if($WebSocketServer->taskworker){   // Task进程
            swoole_set_process_name(self::$processNamePrefix.self::$processNameTask);
        }else{                              // 工作进程
            swoole_set_process_name(self::$processNamePrefix.self::$processNameWorker);
        }
        ResponseMain::serviceWorkerStart($WebSocketServer, $workId);
    }

    /**
     * @param Server $WebSocketServer
     * @param int $workId
     * @return void
     * 进程退出
     */
    public function workerExit(Server $WebSocketServer, int $workId)
    {
        ResponseMain::serviceWorkerExit($WebSocketServer, $workId);
    }

    /**
     * @param Request $request
     * @param Response $response
     * @return void
     * 收到请求
     */
    public function request(Request $request, Response $response)
    {
        ResponseMain::serviceRequest($request, $response);
    }

    /**
     * @param Server $WebSocketServer
     * @param Request $request
     * @return void
     * 回调新连接成功后
     */
    public function open(Server $WebSocketServer, Request $request)
    {
        ResponseMain::serviceOpen($WebSocketServer, $request);
    }

    /**
     * @param Server $WebSocketServer
     * @param Frame $frame
     * @return void
     * 回调接收Websocket消息
     */
    public function message(Server $WebSocketServer, Frame $frame)
    {
        ResponseMain::serviceMessage($WebSocketServer, $frame);
    }

    /**
     * @param Server $WebSocketServer
     * @param int $fd
     * @return void
     * 回调连接已关闭
     */
    public function close(Server $WebSocketServer, int $fd)
    {
        ResponseMain::serviceClose($WebSocketServer, $fd);
    }

    /**
     * @param Server $WebSocketServer
     * @param Task $Task
     * @return void
     * 任务开始执行
     */
    public function taskStart(Server $WebSocketServer, Task $Task)
    {
        ResponseMain::serviceTaskStart($WebSocketServer, $Task);
    }

    /**
     * @param Server $WebSocketServer
     * @param int $taskId
     * @param $finishData
     * @return void
     * 任务执行完成
     */
    public function taskFinish(Server $WebSocketServer, int $taskId, $finishData)
    {
        ResponseMain::serviceTaskFinish($WebSocketServer, $taskId, $finishData);
    }

    /**
     * @param Server $WebSocketServer
     * @return void
     * 重载前回调
     */
    public function reloadBefore(Server $WebSocketServer)
    {
        ResponseMain::serviceReloadBefore($WebSocketServer);
    }

    /**
     * @param Server $WebSocketServer
     * @return void
     * 重载后回调
     */
    public function reloadAfter(Server $WebSocketServer)
    {
        ResponseMain::serviceReloadAfter($WebSocketServer);
    }

    /**
     * @param string $confName
     * @return string
     * getServerRunConfStr
     */
    public static function getServerRunConfStr(string $confName):string
    {
        return strval(self::$serverRunConf[$confName]??'');
    }

    /**
     * @param string $confName
     * @return int
     * getServerRunConfInt
     */
    public static function getServerRunConfInt(string $confName):int
    {
        return intval(self::$serverRunConf[$confName]??0);
    }

    /**
     * @param string $confName
     * @return array
     * getServerRunConfArray
     */
    public static function getServerRunConfArray(string $confName):array
    {
        $confArray = self::$serverRunConf[$confName]??[];
        return is_array($confArray) ? $confArray : [];
    }
}